#include <Arduino.h>
#include <WiFiManager.h>  // https://github.com/tzapu/WiFiManager
#include <nvs_flash.h>
#include <Adafruit_SSD1306.h>

#include "common_util.h"

#include "lcd_fun.h"
#include "oled_fun.h"
#include "button_fun.h"
#include "serial1_fun.h"
#include "tcp_client_fun.h"
#include "tcp_server_fun.h"
#include "udp_server_fun.h"
#include "beep_fun.h"
#include "out_fun.h"

unsigned long SerialBaudRate = 115200;
uint16_t TCPProt = 4418;
uint16_t UDPProt = 4419;

// TCP客户端功能是否开启
uint8_t tcp_client_enable = 0;
char* tcp_client_enable_key = "t_s_e_key";

// TCP客户端功能需要连接的服务器IP
char* TCPServerIP = NULL;  // 服务器 IP 地址
char* TCPServerIP_key = "t_s_ip_key";
// TCP客户端功能需要连接的服务器端口
uint16_t TCPServerPort = 8001;
char* TCPServerPort_key = "t_s_p_key";
int32_t tcp_timeout_ms = 10000;

// 5个按键的默认引脚
uint8_t obtp[5] = { 0, 1, 2, 3, 4 };
// 按钮GIO存储key
char* obtk[5] = { "obtk_0_key", "obtk_1_key", "obtk_2_key", "obtk_3_key", "obtk_4_key" };

// 自由输出引脚
uint8_t outGios[2] = { 5, 6 };
char* outGios_key[2] = { "o_g1_key", "o_g2_key" };

// 线路板LCD统一背光灯开关引脚(高电平触发模块开灯)
uint8_t lcdbgGio = 7;
char* lcdbgGio_key = "l_b_g_key";

// I2C默认引脚
uint8_t I2C_SDA = 8;
char* I2C_SDA_key = "I_D_key";
uint8_t I2C_SCL = 9;
char* I2C_SCL_key = "I_C_key";

// 蜂鸣器引脚
uint8_t beepGio = 10;
char* beepGio_key = "b_g_key";

char* nvs_space = "nvs_space";
nvs_handle_t my_nvs_handle_t;

// 转发是否开启
uint8_t transmit = 0;
char* transmit_key = "t_key";

// 串口1是否开启
uint8_t Serial1_k3k4 = 0;
char* Serial1_k3k4_key = "S1_key";
bool disableSerial1 = true;

WiFiManager wm;

void setup() {
  Serial.begin(SerialBaudRate);

  wm.setConnectTimeout(5);
  wm.setAPCallback(configModeCallback);
  bool res = wm.autoConnect();  // auto generated AP name from chipid
  // res = wm.autoConnect("AutoConnectAP"); // anonymous ap
  // res = wm.autoConnect("AutoConnectAP-zzy"); // password protected ap
  if (!res) {
    Serial.println("Failed to connect");
    //ESP.restart();
  } else {
    //if you get here you have connected to the WiFi
    Serial.println("connected...yeey :)");
  }
  Serial.println("init flash data");

  nvs_flash_init();
  nvs_open(nvs_space, NVS_READONLY, &my_nvs_handle_t);

  // 转发是否开启
  nvs_get_u8(my_nvs_handle_t, transmit_key, &transmit);
  // 串口1是否开启
  nvs_get_u8(my_nvs_handle_t, Serial1_k3k4_key, &Serial1_k3k4);
  if (Serial1_k3k4) {
    disableSerial1 = false;
  }

  nvs_get_u8(my_nvs_handle_t, I2C_SDA_key, &I2C_SDA);
  nvs_get_u8(my_nvs_handle_t, I2C_SCL_key, &I2C_SCL);

  nvs_get_u8(my_nvs_handle_t, tcp_client_enable_key, &tcp_client_enable);
  size_t required_size;
  nvs_get_str(my_nvs_handle_t, TCPServerIP_key, NULL, &required_size);
  Serial.print("required_size:");
  Serial.println(required_size);
  TCPServerIP = (char*)malloc(required_size);
  nvs_get_str(my_nvs_handle_t, TCPServerIP_key, TCPServerIP, &required_size);
  if (TCPServerIP == NULL || strlen(TCPServerIP) == 0) {
    TCPServerIP = "192.168.8.1";
  }
  nvs_get_u16(my_nvs_handle_t, TCPServerPort_key, &TCPServerPort);

  Serial.print("transmit:");
  if (transmit) {
    Serial.print("NO");
  } else {
    Serial.print("OFF");
  }
  Serial.println("  SYS_T0  SYS_T1");

  Serial.print("i2c sda:");
  Serial.print(I2C_SDA);
  Serial.println("   I2C_D03");
  Serial.print("i2c scl:");
  Serial.print(I2C_SCL);
  Serial.println("   I2C_C04");
  Serial.print("i2c clock:");
  Serial.println(400000UL);

  // Wire.setPins(int sda, int scl);
  Wire.setPins(I2C_SDA, I2C_SCL);
  Wire.begin();
  // 设置I2C总线速度为400kHz
  Wire.setClock(400000UL);

  Serial.print("TCP client:");
  if (tcp_client_enable != 1) {
    Serial.print("OFF");
  } else {
    Serial.print("ON");
  }
  Serial.println("  NTC_E0  NTC_E1");
  Serial.print("TCP to server ip:");
  Serial.print(TCPServerIP);
  Serial.println("  NTC_A192.168.1.1");
  Serial.print("TCP to server port:");
  Serial.print(TCPServerPort);
  Serial.println("  NTC_P8001");

  buttonInit();
  serial1Init();
  OLEDInit();
  LCDInit();
  TCPServerInit();
  UDPServerInit();
  TCPClientInit();
  outInit();
  beepInit();
  // 各个init还要读取保存的数据，读取完毕后在关闭
  nvs_close(my_nvs_handle_t);
  nvs_flash_deinit();

  // 屏幕上显示基本提示信息
  String sys_ip_str = WiFi.localIP().toString();
  String sys_prot_str = "TCP:" + String(TCPProt) + " UDP:" + String(UDPProt);
  for (uint8_t i = 0; i < 16; i++) {
    lcdPrint(i, 0, 0, SerialBaudRate);
    lcdPrint(i, 0, 1, sys_ip_str);
    if (lcdr[i] >= 3) {
      lcdPrint(i, 0, 2, sys_prot_str);
    }
    if (lcdr[i] >= 4) {
      lcdPrint(i, 11, 3, "By BH9EQH");
    }
  }
  oledPrint(0, 8, sys_ip_str);
  oledPrint(0, 16, sys_prot_str);
  oledPrint(80, 24, "          By BH9EQH");
  xTaskCreate(SerialTask, "SerialTask", 1024 * 10, NULL, 1, NULL);
}

void loop() {
  delay(20000);
  if (WiFi.status() != WL_CONNECTED) {
    // WiFi断了直接掀桌子
    Serial.println("WiFi disconnected! ESP restart!!!");
    ESP.restart();
  }
  checkTCPServer();
}

void SerialTask(void* ptParam) {
  while (true) {
    //接收串口
    if (Serial.available()) {
      String request = Serial.readStringUntil('\n');
      if (!requestTest(request)) {
        continue;
      }
      requestString(request);
      if (transmit) {
        for (uint8_t i = 0; i < MAX_TCP_CLIENTS; i++) {
          if (serverClients[i] && serverClients[i].connected()) {
            serverClients[i].println(request);
          }
        }
        if (tcp_client.connected()) {
          tcp_client.println(request);
        }
      }
    }
  }
}

void requestString(String request) {
  int len = request.length();
  if (len < 4) {
    return;
  }
  if (requestLCD(request, len)) {
    return;
  }
  if (requestS1(request, len)) {
    return;
  }
  if (requestOLED(request, len)) {
    return;
  }
  if (requestOut(request, len)) {
    return;
  }
  if (requestBeep(request, len)) {
    return;
  }
  if (requestI2C(request, len)) {
    return;
  }
  if (requestSys(request, len)) {
    return;
  }
  if (requestClientTCP(request, len)) {
    return;
  }
  return;
}

uint8_t requestSys(String request, int len) {
  if (!(request.startsWith("SYS_") || request.startsWith("sys_"))) {
    return 0;
  }
  if (len < 5) {
    return 1;
  }
  char c4 = request.charAt(4);
  if ((c4 == 'T' || c4 == 't') && len >= 6) {
    Serial.print("Set transmit:");
    if (request.charAt(5) == '1') {
      saveVlaueU8(transmit_key, 1);
      Serial.println("ON");
    } else {
      saveVlaueU8(transmit_key, 0);
      Serial.println("OFF");
    }
    return 1;
  }

  if (c4 == 'W' || c4 == 'w') {
    wm.resetSettings();
    Serial.println("wm.resetSettings!");
    ESP.restart();
    return 1;
  }
  if (c4 == 'R' || c4 == 'r') {
    Serial.println("restart!");
    ESP.restart();
    return 1;
  }
  if ((c4 == 'K' || c4 == 'k') && len >= 8) {
    uint8_t inedx = request.charAt(5) - '0';
    if (inedx < 0 || inedx > 4) {
      return 1;
    }
    Serial.print("Set key gio:");
    uint8_t gio = (request.charAt(6) - '0') * 10 + (request.charAt(7) - '0');
    saveVlaueU8(obtk[inedx], gio);
    Serial.println(gio);
    return 1;
  }
  return 1;
}

uint8_t requestI2C(String request, int len) {
  if (!(request.startsWith("I2C_") || request.startsWith("i2c_"))) {
    return 0;
  }
  if (len < 5) {
    return 1;
  }
  uint8_t c = request.charAt(4);
  if ((c == 'D' || c == 'd') && len >= 7) {
    uint8_t s = request.charAt(5);
    uint8_t g = request.charAt(6);
    g = (g - '0' + (s - '0') * 10);
    Serial.print("new i2c sda:");
    Serial.println(g);
    saveVlaueU8(I2C_SDA_key, g);
    return 1;
  }
  if ((c == 'C' || c == 'c') && len >= 7) {
    uint8_t s = request.charAt(5);
    uint8_t g = request.charAt(6);
    g = (g - '0' + (s - '0') * 10);
    Serial.print("new i2c scl:");
    Serial.println(g);
    saveVlaueU8(I2C_SCL_key, g);
    return 1;
  }
  return 1;
}

// WiFi连接失败（系统会，进入AP配网模式）
void configModeCallback(WiFiManager* myWiFiManager) {
  // 启动倒计时重启功能。防止单片机比路由器启动的早。然后路由器启动了，但是单片机卡在配网模式下。
  xTaskCreate(taskRestart, "taskRestart", 1024, NULL, 1, NULL);
  // Serial.println("Entered config mode");
  // Serial.println(WiFi.softAPIP());
  // 在这里可以添加额外的配置代码
}

void taskRestart(void* ptParam) {
  Serial.println("taskRestart");
  for (uint8_t i = 120; i > 0; i--) {
    if (i % 5 == 0) {
      Serial.print(i);
      Serial.println("s restart");
    }
    delay(1000);
  }
  ESP.restart();
  vTaskDelete(NULL);
}
